
#include "hitlist/backup.hpp"
#include "hitlist/hitlist_backup.hpp"
#include "hitlist/hitlist.hpp"
#include "hitlist/service.hpp"

#include <iostream>
#include <cstdlib>
#include <sys/time.h>
#include <iomanip>
#include <fas/mux/epoller.hpp>

#define MAX_USER 200000
#define MIDDLE_HITS 10

size_t user_hits(ihitlist* ih,  user_id_t  second_id, size_t max_second, size_t hit_count, double& ago, double step)
{
  size_t result = 0;
  //double rago = ago;
  //double rstep = double(3600*24) / (double(total) /*+ 1*/);
  /*size_t step = 3600*24 / (total + 1);
  if (step==0)
    abort();
  */
  //size_t step = 3600*24 / max_second;
  //step /= 4;
  //if (step==0) step = 1;
  //time_t ago_next = (ago / (3600*24) + 1) * (3600*24);
  /// ago += std::rand() % step;
  for ( size_t i = 0 ; i < hit_count; ++i)
  {
    user_id_t first_id = std::rand() % max_second;
    //time_t hit_time = ago + std::rand() % (3600*24);
    time_t hit_time = ago;
    ih->hit(first_id, second_id, hit_time);
    ++result;
    //if ( ago < ago_next - 1 )
    //  ago += step;
      //++ago;
    //rago += rstep;
    ago += step;
  }
  //ago = rago;
  return result;
};

size_t day_hits(ihitlist* ih, time_t ago, size_t user_per_day,
              size_t max_users, size_t popular_user, size_t star_user,
              size_t middle_hits/*, size_t popular_hits, size_t star_hits*/ )
{
  size_t result = 0;
  size_t total = user_per_day*(middle_hits * 3 );
  double rago = ago;
  double rstep = double(3600*24) / (double(total));
  for ( size_t i = 0 ; i < user_per_day; ++i)
  {
    user_id_t second_id = std::rand() % max_users;
    // Обычные hit's
    result += user_hits(ih, second_id, max_users, middle_hits, rago, rstep);
    // Смотрим популярных hit's
    result += user_hits(ih, second_id, popular_user, middle_hits, rago, rstep);
    // Смотрим очень популярных hit's
    result += user_hits(ih, second_id, star_user, middle_hits, rago, rstep);
  }
  return result;
}


timeval calcspan( timeval start, timeval finish )
{
  timeval result;
  result.tv_sec = finish.tv_sec - start.tv_sec;
  result.tv_usec = 0;
  if ( finish.tv_usec < start.tv_usec )
  {
    result.tv_sec-=1;
    result.tv_usec = 1000000 + finish.tv_usec - start.tv_usec ;
  }
  else
    result.tv_usec = finish.tv_usec - start.tv_usec;
  return result;
}
/*
void showtime( timeval start1, timeval finish1 )
{
  time_t secspan = finish1.tv_sec - start1.tv_sec;
  time_t usecspan = 0;
  if ( finish1.tv_usec < start1.tv_usec )
  {
    secspan-=1;
    usecspan = 1000000 + finish1.tv_usec - start1.tv_usec ;
  }
  else
    usecspan = finish1.tv_usec - start1.tv_usec;
  std::cout << "Время "<< secspan << "." << std::setw(6) << std::setfill('0') << usecspan << std::endl;
};
*/

int main(int argc, const char* argv[])
{
  unsigned short port = argc > 1 ? std::atoi(argv[1]): 12345;
  size_t max_users = argc > 2 ? std::atoi(argv[2]) : MAX_USER;
  size_t middle_hits = argc > 3 ? std::atoi(argv[3]) : MIDDLE_HITS ;
  
  

  hitlist hl;
  hitlist_backup hb;

  hl.backup(&hb);
  backup bj;
  backup bb;
  hb.binary_backup(&bb);
  hb.json_backup(&bj);
  bb.initialize("./backup.test.binary/backup_%d.binary");
  bj.initialize("./backup.test.json/backup_%d.json");


  std::cout << std::endl << "Тестирование логики hitlist на производительность" << std::endl;
  // std::cout << "Соответственно быстродействие демона не может быть выше указанных значений" << std::endl;
  std::cout << std::endl;

  size_t popular_user = max_users / 100;
  size_t star_user = max_users / 1000;
  size_t user_per_day = max_users / 10;

  size_t popular_hits = middle_hits * 10;
  size_t star_hits = middle_hits * 100;

  std::cout << "Всего пользователей: " <<  max_users << std::endl;
  std::cout << "Пользователей ежедневно: " <<  user_per_day << ", которые делают по " << middle_hits << " хитов ежедневно." << std::endl;
  std::cout << "Популярных пользователей: " <<  popular_user << ", которых смотрят по " << popular_hits << " ежедневно." << std::endl;
  std::cout << "Очень популярных пользователей: " <<  star_user << ", которых смотрят по " << star_hits << " ежедневно." << std::endl;
  std::cout << std::endl;
  std::cout << "проверь наличие папок ./backup.test.binary и ./backup.test.json в каталоге запуска " << std::endl;
  std::cout << "и не забудь их очистить перед запуском (обязательно!)" << std::endl;
  std::cout << std::endl;
  std::clog << " << нажми enter для начала инициализации >> " << std::endl;
  std::cin.get();
  hl.test_mode(true);
  hb.test_mode(true);
  // user_hits(&hl, 123, 10000, 1000, 0);
  for ( int i = 30 ; i != 0; --i)
  {
    timeval start, finish;
    std::cout <<std::endl << "День "<<i<<"..."<<std::endl;

    ::gettimeofday(&start, 0);
    size_t result = day_hits(&hl, time(0) - 3600*24*i, user_per_day, max_users, popular_user, star_user, middle_hits);
    ::gettimeofday(&finish, 0);

    std::cout << "Запросов: " << result << ", всего хитов: "<<hl.hits_size()<<", уникальных: " << hl.hosts_size() << std::endl;
    timeval span = calcspan( start, finish );
    std::cout << "Время "<< span.tv_sec << "." << std::setw(6) << std::setfill('0') << span.tv_usec << " сек"<< std::endl;
    if ( span.tv_sec == 0 &&  span.tv_usec == 0)
    {
      std::cout << "Скорость "<< " inf" << " хитов в секунду" << std::endl;
    }
    else
    {
      double rate = (result * 1000000) / ( span.tv_sec * 1000000 + span.tv_usec ) ;
      std::cout << "Скорость "<< rate << " хитов в секунду" << std::endl;
    }
  }
  hl.test_mode(false);
  hb.test_mode(false);
  
  std::cout << std::endl;
  std::cout << "Готово!" << std::endl;
  std::cout << "Далее выборки по пользователям" << std::endl;
  std::cout << "\tids Очень популярных: " << 1 << " ... " << star_user << std::endl;
  std::cout << "\tids популярных: " << star_user << " ... " << popular_user << std::endl;
  std::cout << "\tids остальных: " << popular_user << " ... " << max_users << std::endl;

  std::clog  << " << нажми кнопку >> " << std::endl;
  std::cin.get();
  std::cout << "user_id" << "\t" << "hits_m" << "\t" << "hosts_m" << "\t" << "hits_d" << "\t" << "hosts_d" << std::endl;
  for (size_t i = 0 ; i < 10; i++)
    std::cout << i << "\t" << hl.hits_month(i) << "\t" << hl.hosts_month(i) << "\t" << hl.hits_day(i) << "\t" << hl.hosts_day(i) << std::endl;

  std::clog << " << и еще раз нажми кнопку >> " << std::endl;
  std::cin.get();
    std::cout << "user_id" << "\t" << "hits_m" << "\t" << "hosts_m" << "\t" << "hits_d" << "\t" << "hosts_d" << std::endl;
  for (size_t i = star_user ; i < star_user + 10; i++)
    std::cout << i << "\t" << hl.hits_month(i) << "\t" << hl.hosts_month(i) << "\t" << hl.hits_day(i) << "\t" << hl.hosts_day(i)<< std::endl;

  std::clog << " << и еще раз нажми кнопку >> " << std::endl;
  std::cin.get();
  std::cout << "user_id" << "\t" << "hits_m" << "\t" << "hosts_m" << "\t" << "hits_d" << "\t" << "hosts_d" << std::endl;
  for (size_t i = popular_user ; i < popular_user + 10; i++)
    std::cout << i << "\t" << hl.hits_month(i) << "\t" << hl.hosts_month(i) << "\t" << hl.hits_day(i) << "\t" << hl.hosts_day(i)<< std::endl;
  
  
  /// backup
  ///
  std::cout << std::endl;
  std::cout << "Тестирование востановления" << std::endl;
  std::cout << "Всего хитов: "<< hl.hits_size() <<", уникальных: " << hl.hosts_size() << std::endl;
  std::clog << " << нажми кнопку >> " << std::endl;
  std::cin.get();
  std::cout << std::endl;
  std::cout << "Востановление из binary backup" << std::endl;
  std::cout << "Очищаем..." << std::endl;
  hl.clear();
  std::cout << "Всего хитов: "<< hl.hits_size() <<", уникальных: " << hl.hosts_size() << std::endl;
  timeval start, finish;
  ::gettimeofday(&start, 0);
  hb.restore(true, &hl, 30);
  ::gettimeofday(&finish, 0);
  timeval span = calcspan( start, finish );
  std::cout << "Время "<< span.tv_sec << "." << std::setw(6) << std::setfill('0') << span.tv_usec << " сек"<< std::endl;
  std::cout << "Всего хитов: "<< hl.hits_size() <<", уникальных: " << hl.hosts_size() << std::endl;
  std::cout << std::endl;
  std::cout << "Востановление из json backup" << std::endl;
  std::cout << "Очищаем..." << std::endl;
  hl.clear();
  std::cout << "Всего хитов: "<< hl.hits_size() <<", уникальных: " << hl.hosts_size() << std::endl;
  ::gettimeofday(&start, 0);
  hb.restore(false, &hl, 30);
  ::gettimeofday(&finish, 0);
  span = calcspan( start, finish );
  std::cout << "Время "<< span.tv_sec << "." << std::setw(6) << std::setfill('0') << span.tv_usec << " сек"<< std::endl;
  std::cout << "Всего хитов: "<< hl.hits_size() <<", уникальных: " << hl.hosts_size() << std::endl;
  
  std::cout << "Готово!" << std::endl;
  std::clog << " << последний раз нажми кнопку >> " << std::endl;
  std::cin.get();
  /// ///////////////////////////////////////
  /// ///////////////////////////////////////
  /// запускаем сервер
  std::cout << "запуск сервера порт " << port << std::endl;
  am::epoller mux;
  service s;
  s.initialize(&mux, &hl);
  s.tcp_start("", port);
  s.udp_start("", port);
  std::cout << "Для тестирования сетевого взаимодействия запусти client_benchmark" << std::endl;
  std::cout << "(текущий процесс останавливать не надо)" << std::endl;
  for (;;)
  {
    mux.select(300);
    std::pair<size_t, size_t> idle = hl.idle();
    /*if ( idle.first )
      std::cout << "idle clean users: " << idle.first << " hits: " << idle.second << std::endl;*/
    hb.flush();
  }
  return 0;
}
